#include "housingdbms.h"

#include <QFileInfo>
#include <QMessageBox>
#include <QDebug>
#include <QVariantList>
#include <QVariant>
#include <QDate>

LorisMessenger HousingDBMS::m_msngr;
LorisMessenger HousingDBMS::m_fmsngr;
LorisMessenger HousingDBMS::m_msngr_e;


HousingDBMS::HousingDBMS(QObject *parent):
    QObject(parent),
    m_state_ok(false),
    m_write_enabled(false),
    mp_models(NULL)
{
    if(!m_msngr.isOpen())
        m_msngr.open(QIODevice::WriteOnly);
    if(!m_msngr_e.isOpen())
        m_msngr_e.open(QIODevice::WriteOnly);
    if(!m_fmsngr.isOpen())
        m_fmsngr.open(QIODevice::WriteOnly);

    connect(&m_msngr, SIGNAL(message_string(QString)), this, SIGNAL(info_message(QString)));
    connect(&m_msngr_e, SIGNAL(message_string(QString)), this, SIGNAL(error_message(QString)));
    connect(&m_fmsngr, SIGNAL(message_string(QString)), this, SIGNAL(fast_message(QString)));
    mqstr_db_vers = HDBMS_VERSION;
}


//-----------------------------------------------------------------------------------------------------

bool HousingDBMS::check_home_access()
{

    QFileInfo fi_home(m_home_dir.absolutePath());
    if(!fi_home.isDir())
    {
        lakhMsg()<<"Home path is not a directory";
        return false;
    }
    m_write_enabled = true;
    if(!fi_home.permission(QFile::WriteUser|QFile::ExeUser))
    {
        lakhMsg()<<"User doesn\'t has a write permissions. Read-Only mode set";
        m_write_enabled = false;
    }
    if(!fi_home.permission(QFile::ReadUser))
    {
        lakhMsg()<<"User doesn\'t has access rights at home directory";
        return false;
    }
    return true;
}

//-----------------------------------------------------------------------------------------------------
void HousingDBMS::set_models(ModelContainerBase *p_models)
{
    mp_models = p_models;
    mp_models->set_dbms(this);
}

//-----------------------------------------------------------------------------------------------------

bool HousingDBMS::open_db(HousingSettings &hs)
{
    if(m_state_ok) return true;
    m_home_dir.setPath(hs.str_home_path);
    if(!check_home_access()) return false;
    QStringList fld_names_lst;
    QList<int> index_lst;
    QList<QVariant::Type> fld_types_lst;
    //************ check version ************
    QString qstr_ctrl_filename = hs.str_home_path + "/hsdbms.ctrl";
    QFile qf_ctrl;
    qf_ctrl.setFileName(qstr_ctrl_filename);
    QDataStream qds_ctrl;
    if(QFile::exists(qstr_ctrl_filename))
    {
        QString qstr_vers;
        qf_ctrl.open(QIODevice::ReadOnly);
        qds_ctrl.setDevice(&qf_ctrl);
        qds_ctrl>>qstr_vers;
        qf_ctrl.close();
        qDebug()<<"DB VERSION FOUND: "<<qstr_vers;
        if(qstr_vers != mqstr_db_vers)
        {
            lakhMsgE()<<RUSTR("Ошибка. Данная версия базы данных не поддерживается.");
            qCritical()<<"unsupported db version!";
            return false;
        }
    }
    else
    {
        lakhMsgE()<<RUSTR("Внимание. Не найден файл контроля версии БД.");
        QMessageBox  msgBox;
        msgBox.setText(RUSTR("Не найден файл контроля версии БД."));
        msgBox.setInformativeText(RUSTR("Создать новую БД? \n(Будут удалены все старые файлы БД в домашнем каталоге)"));
        msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::No);
        int ret = msgBox.exec();
        if(ret == QMessageBox::No) return false;
        QString qstr_vers;
        qf_ctrl.open(QIODevice::WriteOnly);
        qds_ctrl.setDevice(&qf_ctrl);
        qds_ctrl<<mqstr_db_vers;
        qf_ctrl.close();
        QStringList qstrl_name_filter;
        qstrl_name_filter<<"*.db";
        QFileInfoList qfil =  m_home_dir.entryInfoList(qstrl_name_filter, QDir::Files|QDir::NoDotDot,QDir::NoSort);
        foreach(QFileInfo qfi, qfil)
        {
        m_home_dir.remove(qfi.fileName());
        }
    }

    lakhMsg()<<RUSTR("Версия базы данных: %1").arg(mqstr_db_vers);

    //************ init data bases *****************

    m_db_names<<"streets"<<"houses"<<"apartments"<<"tenants"<<"leases"<<"families"<<"decmasters"<<"payments"<<"bills";
    for(int i = 0; i < LAKDB_NUM; i++)
    {
        fld_names_lst.clear();
        index_lst.clear();
        fld_types_lst.clear();
        switch(i)
        {
        case LAKDB_STREETS:
            fld_names_lst<<"street"<<"type";
            fld_types_lst<<QVariant::String<<QVariant::Int;
            index_lst<<FLD_STDB_NAME;
            lakhMsg()<<RUSTR("Группа данных \"УЛИЦЫ\"");
            break;
        case LAKDB_HOUSES:
            fld_names_lst<<"st_index"<<"name"<<"space"<<"facilities"<<"tariff"<<"type"<<"comments";
            fld_types_lst<<QVariant::ULongLong<<QVariant::String<<QVariant::Double<<QVariant::Int<<QVariant::Double<<QVariant::Int<<QVariant::String;
            index_lst<<FLD_HSDB_STINDEX<<FLD_HSDB_NAME<<FLD_HSDB_SPC<<FLD_HSDB_TYPE;
            lakhMsg()<<RUSTR("Группа данных \"ДОМА\" ");
            break;
        case LAKDB_APARTMENTS:
            fld_names_lst<<"hs_index"<<"number"<<"total_space"<<"live_space"<<"rcount"<<"comments"<<"prvt-dates";
            fld_types_lst<<QVariant::ULongLong<<QVariant::String \
                         <<QVariant::Double<<QVariant::Hash \
                         <<QVariant::Int<<QVariant::String<<QVariant::Hash;
            index_lst<<FLD_APDB_HSINDEX<<FLD_APDB_NUMBER;
            lakhMsg()<<RUSTR("Группа данных \"ЖИЛЫЕ ПОМЕЩЕНИЯ\" ");
            break;
        case LAKDB_TENANTS:
            fld_names_lst<<"family"<<"name"<<"p_name"<<"dob"<<"passnum"<<"passdate"<<"passdep"<<"regadr"<<"comments";
            fld_types_lst<<QVariant::String<<QVariant::String<<QVariant::String<<QVariant::Date<<QVariant::String<<QVariant::Date<<QVariant::String<<QVariant::String<<QVariant::String;
            index_lst<<FLD_TEN_FAM;
            lakhMsg()<<RUSTR("Группа данных \"НАНИМАТЕЛИ\" ");
            break;
        case LAKDB_LEASES:
            fld_names_lst<<"tindex"<<"aindex"<<"spaces_lst"<<"s_date"<<"e_date"<<"number"<<"comments" \
                         <<"lender-head"<<"lender-head-fio"<<"stop reason"<<"dec_lst"<<"ddates_lst"<<"dnum_lst";
            fld_types_lst<<QVariant::ULongLong<<QVariant::ULongLong<<QVariant::StringList<<QVariant::Date<<QVariant::Date \
                         <<QVariant::Int<<QVariant::String <<QVariant::String<<QVariant::String<<QVariant::String \
                         <<QVariant::List<<QVariant::List<<QVariant::List;
            index_lst<<FLD_LS_TINDEX<<FLD_LS_AINDEX<<FLD_LS_SDATE<<FLD_LS_EDATE<<FLD_LS_NUM;
            lakhMsg()<<RUSTR("Группа данных \"ДОГОВОРА\" ");
            break;
        case LAKDB_FAMILIES:
            fld_names_lst<<"tindex"<<"family"<<"name"<<"p_name"<<"dob"<<"relation";
            fld_types_lst<<QVariant::ULongLong<<QVariant::String<<QVariant::String<<QVariant::String<<QVariant::Date<<QVariant::String;
            index_lst<<FLD_FAM_TINDEX;
            lakhMsg()<<RUSTR("Группа данных \"СОСТАВ СЕМЬИ\" ");
            break;
        case LAKDB_DECMASTERS:
            fld_names_lst<<"name";
            fld_types_lst<<QVariant::String;
            lakhMsg()<<RUSTR("Группа данных \"РЕШЕНИЯ\'' ");
            break;
        case LAKDB_PAYMENTS:
            fld_names_lst<<"bindex"<<"aindex"<<"lindex"<<"targed date"<<"ammount"<<"spaces-ids-list"<<"date";
            fld_types_lst<<QVariant::ULongLong<<QVariant::ULongLong<<QVariant::ULongLong \
                             <<QVariant::Date<<QVariant::Double<<QVariant::StringList<<QVariant::Date;
            index_lst<<FLD_PAY_BINDEX<<FLD_PAY_AINDEX<<FLD_PAY_LINDEX<<FLD_PAY_TARGETDATE<<FLD_PAY_DATE;
            lakhMsg()<<RUSTR("Группа данных \"ПЛАТЕЖИ\" ");
            break;
        case LAKDB_BILLS:
            fld_names_lst<<"aindex"<<"targed date"<<"spaces-ids-lst"<<"ammount"<<"lindex"<<"tariff";
            index_lst<<FLD_BILL_AINDEX<<FLD_BILL_TARGETDATE<<FLD_BILL_LINDEX;
            fld_types_lst<<QVariant::ULongLong<<QVariant::Date<<QVariant::StringList<<QVariant::Double<<QVariant::ULongLong<<QVariant::Double;
            lakhMsg()<<RUSTR("Группа данных \"КВИТАНЦИИ\"");
            break;
        }
        m_dbs[i].setupFields(fld_names_lst,fld_types_lst, index_lst);
        int ret;
        ret = m_dbs[i].initDb(m_home_dir.absolutePath(), m_db_names.at(i), !m_write_enabled);
        if(!ret) lakhMsg()<<RUSTR("Подключено.");
        else lakhMsgE()<<RUSTR("Ошибка подключения.");

    }
    m_state_ok = true;
    qDebug()<<"DBMS ready";
    lakhMsg()<<RUSTR("База данных загружена.");
    return true;
}

//-----------------------------------------------------------------------------------------------------

QVariant HousingDBMS::db_put(int db_index, const QHash<int, QVariant> &qh_data, const QVariant &qv_key)
{
    if(!m_state_ok)
    {
       qCritical()<<"dbms isn't ok";
        return QVariant();
    }
    if((db_index >= LAKDB_NUM) || (db_index <0))
    {
        qWarning()<<"db index out of range";
        return QVariant();
    }
    if(!m_dbs[db_index].is_valid_data(qh_data))
    {
        qWarning()<<"not valid data";
        return QVariant();
    }
    if(qv_key.isNull())
        return m_dbs[db_index].putData(qh_data);
    else
    {
      m_dbs[db_index].removeData(qh_data, qv_key);
//       qDebug()<<"db_put, update mode. qv_key"<<qv_key<<"removed";
        return m_dbs[db_index].putData(qh_data, &qv_key);
    }
}


//-----------------------------------------------------------------------------------------------------


SLEE_HDB_REC_LST HousingDBMS::get_db_data(int db_index, QVariantList &qv_pkeys_lst)
{
    SLEE_HDB_REC_LST qh_data_lst;
    if(!m_state_ok) return qh_data_lst;
    if((db_index >= LAKDB_NUM) || (db_index <0)) return qh_data_lst;
    qv_pkeys_lst = m_dbs[db_index].findDataEq(PRIM_FLD,QVariant(),&qh_data_lst);
    return qh_data_lst;
}


//-----------------------------------------------------------------------------------------------------

SLEE_HDB_REC HousingDBMS::get_data(int db_index, const QVariant &qv_key)
{
    SLEE_HDB_REC qh_ret_data;
    if(!m_state_ok) return qh_ret_data;
    if((db_index >= LAKDB_NUM) || (db_index <0)) return qh_ret_data;
    SLEE_HDB_REC_LST qh_data_lst;
    m_dbs[db_index].getData(qh_data_lst, qv_key);
    if(!qh_data_lst.size())
    {
        qWarning()<<"HousingDBMS::get_data() get_data from db. null result. possibly incorrect index. db_name"<<m_db_names.at(db_index)<<"data index"<<qv_key.toULongLong();
        return qh_ret_data;
    }
    return qh_data_lst.at(0);
}

//-----------------------------------------------------------------------------------------------------

SleeQHash_BDB *HousingDBMS::get_db(int db_index)
{
    if(!m_state_ok) return NULL;
    if((db_index >= LAKDB_NUM) || (db_index <0)) return NULL;
    return &m_dbs[db_index];
}


//-----------------------------------------------------------------------------------------------------


QStringList HousingDBMS::get_db_fld_names(int db_index)
{
    if(!m_state_ok) return QStringList();
    if((db_index >= LAKDB_NUM) || (db_index <0)) return QStringList();
    return m_dbs[db_index].get_fld_names();
}

QStringList HousingDBMS::get_street_types(bool do_full)
{
    QStringList ret_list;
    if(do_full)
    {
        ret_list<<RUSTR("улица") \
                    <<RUSTR("переулок") \
                    <<RUSTR("территория") \
                    <<RUSTR("проезд") \
                    <<RUSTR("проспект") \
                    <<RUSTR("площадь");
        return ret_list;
    }
    ret_list<<RUSTR("ул.") \
                <<RUSTR("пер.") \
                <<RUSTR("терр.") \
                <<RUSTR("пр-д") \
                <<RUSTR("пр-кт") \
                <<RUSTR("пл.");
    return ret_list;

}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_street_type(int i)
{
    QStringList qstrl_types = get_street_types();
    if(i >= qstrl_types.size()) return QString();
    return qstrl_types.at(i);
}

//-----------------------------------------------------------------------------------------------------

bool HousingDBMS::can_remove(int db_index, QVariant qv_key)
{
    QVariantList qv_keys_list;
    QMessageBox  msgBox;
    msgBox.setText(RUSTR("Запрос на удаление данных."));
    msgBox.setInformativeText(RUSTR("Вы действительно хотите удалить запись?"));
    msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
    msgBox.setDefaultButton(QMessageBox::No);
    switch(db_index)
    {
    case LAKDB_STREETS:
        qv_keys_list = m_dbs[LAKDB_HOUSES].findDataEq(FLD_HSDB_STINDEX,qv_key);
        if(qv_keys_list.size())
        {
            lakhMsgE()<<RUSTR("Внимание! Данная запись не может быть удалена, т.к. БД \"дома\" содержит ссылки на удаляемую запись.");
            lakhMsgE()<<RUSTR("Предварительно удалите соответствующие записи из БД \"дома\"");
            qWarning()<<"house db has references to this record";
            return false;
        }
        break;
    case LAKDB_HOUSES:
        qv_keys_list = m_dbs[LAKDB_APARTMENTS].findDataEq(FLD_APDB_HSINDEX,qv_key);
        if(qv_keys_list.size())
        {
            lakhMsgE()<<RUSTR("Внимание! Данная запись не может быть удалена, т.к. БД \"жилые помещения\" содержит ссылки на удаляемую запись");
            lakhMsgE()<<RUSTR("Предварительно удалите соответствующие записи из БД \"жилые помещения\"");
            qWarning()<<"apartments db has references to this record";
            return false;
        }
        break;
    case LAKDB_APARTMENTS:
        qv_keys_list = m_dbs[LAKDB_LEASES].findDataEq(FLD_LS_AINDEX,qv_key);
        if(qv_keys_list.size())
        {
            lakhMsgE()<<RUSTR("Внимание! Данная запись не может быть удалена, т.к. в БД существуют договора на это жилое помещение");
            lakhMsgE()<<RUSTR("Предварительно удалите соответствующие записи из БД \"договора\"");
            qWarning()<<"leases db has references to this record";
            return false;
        }
        break;

    case LAKDB_TENANTS:
        qv_keys_list = m_dbs[LAKDB_LEASES].findDataEq(FLD_LS_TINDEX,qv_key);
        if(qv_keys_list.size())
        {
            lakhMsgE()<<RUSTR("Внимание! Данная запись не может быть удалена, т.к. в БД существуют договора с этим человеком");
            lakhMsgE()<<RUSTR("Предварительно удалите соответствующие записи из БД \"договора\"");
            qWarning()<<"leases db has references to this record";
            return false;
        }
        qv_keys_list = m_dbs[LAKDB_FAMILIES].findDataEq(FLD_FAM_TINDEX, qv_key);
        if(qv_keys_list.size())
        {
            lakhMsgE()<<RUSTR("Внимание! Данная запись не может быть удалена, т.к. в БД существуют члены семьи данного нанимателя");
            lakhMsgE()<<RUSTR("Предварительно удалите соответствующие записи из БД \"состав семьи\"");
            qWarning()<<"families db has references to this record";
            return false;
        }

        break;
    case LAKDB_LEASES:
        break;
    case LAKDB_DECMASTERS:
        break;
    }
    int ret = msgBox.exec();
    if(ret == QMessageBox::No) return false;
    return true;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_person_fio_inf(const QHash<int, QVariant> &qh_data, bool full_fio,  bool short_year)
{
    QString str;
    str += (qh_data.value(FLD_PERS_FAMILY).toString() + " ");
    if(!full_fio)
    {
        str += (QString(qh_data.value(FLD_PERS_NAME).toString().at(0)) + ". ");
        str += (QString(qh_data.value(FLD_PERS_PNAME).toString().at(0)) + ". ");
    }
    else
    {
        str += (qh_data.value(FLD_PERS_NAME).toString() + " ");
        str += (qh_data.value(FLD_PERS_PNAME).toString() + " ");
    }
    if(!short_year)
        str += (qh_data.value(FLD_PERS_DOB).toDate().toString("  dd.MM.yyyy") + RUSTR(" г.р."));
    else
        str += (qh_data.value(FLD_PERS_DOB).toDate().toString("  dd.MM.yy") + RUSTR(" г.р."));
    return str;
}

//-----------------------------------------------------------------------------------------------------

QStringList HousingDBMS::get_fam_relations()
{
    QStringList strlst;
    strlst<<RUSTR("ЖЕНА") \
          <<RUSTR("МУЖ") \
          <<RUSTR("СЫН") \
          <<RUSTR("ДОЧЬ") \
          <<RUSTR("ВНУК") \
          <<RUSTR("ВНУЧКА") \
          <<RUSTR("ТЕСТЬ") \
          <<RUSTR("ТЕЩА") \
          <<RUSTR("СВЕКР") \
          <<RUSTR("СВЕКРОВЬ") \
          <<RUSTR("ДЕДУШКА") \
          <<RUSTR("БАБУШКА") \
          <<RUSTR("ДЯДЯ") \
          <<RUSTR("ТЕТЯ") \
          <<RUSTR("ПЛЕМЯННИК") \
          <<RUSTR("ПЛЕМЯННИЦА") \
          <<RUSTR("БРАТ") \
          <<RUSTR("СЕСТРА") \
          <<RUSTR("ДВ.БРАТ") \
          <<RUSTR("ДВ.СЕСТРА") \
          <<RUSTR("ПАСЫНОК") \
          <<RUSTR("ПАДЧЕРИЦА") \
          <<RUSTR("ГР.МУЖ") \
          <<RUSTR("ГР.ЖЕНА");
    return strlst;
}

//-----------------------------------------------------------------------------------------------------

int HousingDBMS::get_house_type(const QVariant &qv_h_index)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_HOUSES, qv_h_index);
    if(qh_data.isEmpty()) return -1;
    return qh_data.value(FLD_HSDB_TYPE).toInt();
}

//-----------------------------------------------------------------------------------------------------

QStringList HousingDBMS::get_house_types(bool do_full)
{
        QStringList qstrl;
        if(do_full)
            qstrl<<RUSTR("многоквартирный дом") \
                    <<RUSTR("общежитие") \
                    <<RUSTR("коммунальное жилье");
        else
            qstrl<<RUSTR("мн-кв") \
                    <<RUSTR("общ.") \
                    <<RUSTR("комм.");
        return qstrl;
}

//-----------------------------------------------------------------------------------------------------


int     HousingDBMS::get_apartment_htype(const QVariant &qv_ap_index)
{
   SLEE_HDB_REC qh_data = get_data(LAKDB_APARTMENTS, qv_ap_index);
   if(qh_data.isEmpty()) return -1;
   QVariant qv_h_index = qh_data.value(FLD_APDB_HSINDEX);
   return  get_house_type(qv_h_index);
}


//-----------------------------------------------------------------------------------------------------




SLEE_HDB_REC_LST HousingDBMS::get_leases_for(const QVariant &qv_ap_index, const QVariant &qv_date)
{
    SleeQHash_Constrain date_constr;
    SleeQHash_Constrain *p_constr = NULL;
    if(!qv_date.isNull())
    {
        date_constr.append_data(qv_date);
        date_constr.append_fld(FLD_LS_SDATE);
        date_constr.append_fld(FLD_LS_EDATE);
        date_constr.set_process_string("($0# < $0% < $1#) | ($0# = $0%) | ($1# = $0%)");
        p_constr = &date_constr;
    }
    SLEE_HDB_REC_LST qh_ret_lst;
    m_dbs[LAKDB_LEASES].findDataEq(FLD_LS_AINDEX,qv_ap_index,&qh_ret_lst,p_constr);
    return qh_ret_lst;
}

//-----------------------------------------------------------------------------------------------------

SLEE_HDB_REC_LST HousingDBMS::get_leases_at(const QVariant &qv_date)
{
    SleeQHash_Constrain date_constr;
    date_constr.append_data(qv_date);
    date_constr.append_fld(FLD_LS_SDATE);
    date_constr.append_fld(FLD_LS_EDATE);
    date_constr.set_process_string("($0# < $0% < $1#) | ($0# = $0%) | ($1# = $0%)");
    SLEE_HDB_REC_LST qh_ret_lst;
    m_dbs[LAKDB_LEASES].findDataRange(-1,NULL,true,NULL,true,&qh_ret_lst,&date_constr);
    return qh_ret_lst;
}

//-----------------------------------------------------------------------------------------------------


SLEE_HDB_REC_LST HousingDBMS::get_leases_in(const QVariant &qv_ap_index, const QVariant &qv_sdate, const QVariant &qv_edate)
{
    SleeQHash_Constrain date_constr;
    date_constr.append_data(qv_sdate);
    date_constr.append_data(qv_edate);
    date_constr.append_fld(FLD_LS_SDATE);
    date_constr.append_fld(FLD_LS_EDATE);

    date_constr.set_process_string("($0% < $0# < $1%) | ($0% < $1# < $1%) | ($0# < $1% < $1#)");
    date_constr.append_process_string("| ($0% = $0#) | ($0% = $1#) | ($1% = $0#) | ($1% = $1#)");

    SLEE_HDB_REC_LST qh_ret_lst;
    m_dbs[LAKDB_LEASES].findDataEq(FLD_LS_AINDEX,qv_ap_index,&qh_ret_lst,&date_constr);
    return qh_ret_lst;
}


//-----------------------------------------------------------------------------------------------------

SLEE_HDB_REC_LST HousingDBMS::get_leases_at_month(const QVariant &qv_date)
{
    QDate qd_req = qv_date.toDate();
    int day = qd_req.day();
    QVariant qv_middle_date = qd_req.addDays(-day+15);

    SleeQHash_Constrain date_constr;
    date_constr.append_data(qv_middle_date);
    date_constr.append_fld(FLD_LS_SDATE);
    date_constr.append_fld(FLD_LS_EDATE);

    date_constr.set_process_string("($0# < $0% < $1#) | ($0# = $0% < $1#) | ($0# < $0% = $1#)");

    SLEE_HDB_REC_LST qh_ret_lst;

    m_dbs[LAKDB_LEASES].findDataRange(-1,NULL,true,NULL,true,&qh_ret_lst,&date_constr);

    return qh_ret_lst;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_tenant_fio(const QVariant &qv_t_index, bool full_fio)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_TENANTS, qv_t_index);
    return get_tenant_fio(qh_data, full_fio);
}


//-----------------------------------------------------------------------------------------------------

QString  HousingDBMS::get_tenant_dob_str(const QVariant &qv_t_index, bool do_wide_format)
{


    SLEE_HDB_REC qh_data = get_data(LAKDB_TENANTS, qv_t_index);
    if(qh_data.isEmpty()) return QString();
    return get_tenant_dob_str(qh_data,do_wide_format);
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_house_addr(const QHash<int, QVariant> &qh_data)
{
    QString qstr_ret = RUSTR("д.") + qh_data.value(FLD_HSDB_NAME).toString();
    QVariant qv_s_index = qh_data.value(FLD_HSDB_STINDEX);
    SLEE_HDB_REC qh_s_data = get_data(LAKDB_STREETS, qv_s_index);
    if(qh_s_data.isEmpty()) return QString();
    qstr_ret = get_street_type(qh_s_data.value(FLD_STDB_TYPE).toInt()) + " " + \
            qh_s_data.value(FLD_STDB_NAME).toString() + ", " + qstr_ret;
    return qstr_ret;
}

//-----------------------------------------------------------------------------------------------------

QString  HousingDBMS::get_house_addr(const QVariant &qv_h_index)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_HOUSES, qv_h_index);
    if(qh_data.isEmpty()) return QString();
    return get_house_addr(qh_data);
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_apartment_addr(const QHash<int, QVariant> &qh_data)
{
    QString qstr_ret;
    QString qstr_h_addr = get_house_addr(qh_data.value(FLD_APDB_HSINDEX));
    int h_type = get_house_type(qh_data.value(FLD_APDB_HSINDEX));
    switch(h_type)
    {
    case 0: qstr_ret = RUSTR("кв. "); break;
    case 1: qstr_ret = RUSTR("комн. "); break;
    case 2: qstr_ret = RUSTR("кв. "); break;
    }
    qstr_ret += qh_data.value(FLD_APDB_NUMBER).toString();
    qstr_ret = qstr_h_addr + ", " + qstr_ret;
    return qstr_ret;
}

//-----------------------------------------------------------------------------------------------------

QString  HousingDBMS::get_apartment_addr(const QVariant &qv_ap_index)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_APARTMENTS, qv_ap_index);
    if(qh_data.isEmpty()) return QString();
    return get_apartment_addr(qh_data);
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_apartment_info(const SLEE_HDB_REC &qh_data, bool add_address )
{
    QString qstr_ret;
    SLEE_HDB_REC qh_h_data = get_data(LAKDB_HOUSES, qh_data.value(FLD_APDB_HSINDEX));
    QString qstr_h_type = get_house_type(qh_h_data.value(FLD_HSDB_TYPE).toInt());
    if(add_address)
    {
        qstr_ret = get_apartment_addr(qh_data) ;
    }
    else
    {
        qstr_ret = RUSTR("к. ") + QString::number(qh_data.value(FLD_APDB_NUMBER).toInt());
    }
    QVariantHash qvh_spaces = qh_data.value(FLD_APDB_LSPSHASH).toHash();
    double lspace = get_apartment_lspace(qvh_spaces);


    qstr_ret  = qstr_ret + " ("+qstr_h_type+")  "+ RUSTR("ж.пл. ") + QString::number(lspace) + RUSTR("кв.м. ");
    qstr_ret = qstr_ret + qh_data.value(FLD_APDB_RCOUNT).toString() + RUSTR(" комн.");
    return qstr_ret;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_apartment_info(const QVariant &qv_ap_index, bool add_address )
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_APARTMENTS, qv_ap_index);
    if(qh_data.isEmpty()) return QString();
    return get_apartment_info(qh_data, add_address);
}

//-----------------------------------------------------------------------------------------------------

double HousingDBMS::get_leases_space(const SLEE_HDB_REC & qh_data)
{
    QStringList qsl_sp_ids_lst;
    qsl_sp_ids_lst = qh_data.value(FLD_LS_SPSLST).toStringList();
    double space = get_apartment_lspace(qh_data.value(FLD_LS_AINDEX), qsl_sp_ids_lst);
    return space;
}

//-----------------------------------------------------------------------------------------------------

double HousingDBMS::get_leases_space(const QVariant &qv_ls_index)
{
    SLEE_HDB_REC qh_data =get_data(LAKDB_LEASES, qv_ls_index);
    if(qh_data.isEmpty()) return 0.0;
    return get_leases_space(qh_data);
}

//-----------------------------------------------------------------------------------------------------

QString  HousingDBMS::get_lease_info(const SLEE_HDB_REC &qh_data)
{
    QString qstr_ret;
    qstr_ret += RUSTR("Договор №");
    qstr_ret += qh_data[FLD_LS_NUM].toString();
    qstr_ret += " (";
    qstr_ret += qh_data[FLD_LS_SDATE].toDate().toString("dd.MM.yyyy");
    qstr_ret += " - ";
    qstr_ret += qh_data[FLD_LS_EDATE].toDate().toString("dd.MM.yyyy");
    qstr_ret += ")\n";
    qstr_ret += RUSTR("Наниматель: ");
    qstr_ret += get_tenant_fio(qh_data[FLD_LS_TINDEX]);
    qstr_ret += " ";
    qstr_ret += get_tenant_dob_str(qh_data[FLD_LS_TINDEX]);
    qstr_ret += RUSTR(" г.р.");
    qstr_ret += "\n";
    qstr_ret += RUSTR("Жил. пом.:");
    qstr_ret += get_apartment_addr(qh_data[FLD_LS_AINDEX]);
    qstr_ret += "\n";
    qstr_ret += RUSTR("Арендуемая пл. ");
    qstr_ret += QString::number(get_leases_space(qh_data));
    qstr_ret += RUSTR("кв.м.");
    return qstr_ret;
}

//-----------------------------------------------------------------------------------------------------

QString  HousingDBMS::get_lease_info(const QVariant &qv_l_index)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_LEASES, qv_l_index);
    if(qh_data.isEmpty()) return QString();
    return get_lease_info(qh_data);
}

//-----------------------------------------------------------------------------------------------------

QStringList HousingDBMS::get_facilities_list(int facilities, bool do_compact)
{
    QStringList all_fac_list, ret_fac_list;
    if(!do_compact)
    {
        all_fac_list<<RUSTR("электроснабжение") \
                   <<RUSTR("газоснабжение") \
                  <<RUSTR("холодное водоснабжение") \
                 <<RUSTR("горячее водоснабжение") \
                <<RUSTR("водоотведение (канализация)") \
               <<RUSTR("отопление");
    }
    else
    {
        all_fac_list<<RUSTR("эл.") \
                   <<RUSTR("газ.") \
                  <<RUSTR("хол. в.") \
                 <<RUSTR("гор. в.") \
                <<RUSTR("кан.") \
               <<RUSTR("отопл.");
    }
    for(int i = 0; i< all_fac_list.size(); i++)
    {
        int mask = 1<<i;
        if(facilities & mask)
        {
            ret_fac_list.append(all_fac_list.at(i));
        }
    }
    return ret_fac_list;
}


//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_house_type(int type)
{
    switch(type)
    {
    case 0: return RUSTR("мн-кв.");
    case 1: return RUSTR("общ.");
    case 2: return RUSTR("комм.");
    }
    return QString();
}

//-----------------------------------------------------------------------------------------------------
QString HousingDBMS::get_house_info(const QHash<int, QVariant> &qh_data )
{
    QString ret_str = RUSTR("д. ");
    ret_str+=qh_data.value(FLD_HSDB_NAME).toString();
    ret_str+=", (";
    ret_str+=get_house_type(qh_data.value(FLD_HSDB_TYPE).toInt());
    ret_str+="), ";
    ret_str+=qh_data.value(FLD_HSDB_SPC).toString();
    ret_str+=RUSTR(" кв.м.,");
    ret_str+=RUSTR(" тариф: ");
    ret_str+=qh_data.value(FLD_HSDB_TARIFF).toString();
    return ret_str;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_house_info(const QVariant &qv_h_key)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_HOUSES, qv_h_key);
    return get_house_info(qh_data);
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_street_info(const SLEE_HDB_REC &qh_data, bool do_reversed)
{

    QString ret_str;
    ret_str = get_street_type(qh_data.value(FLD_STDB_TYPE).toInt());
    if(!do_reversed)
    {
        ret_str+=" ";
        ret_str+=qh_data.value(FLD_STDB_NAME).toString();
    }
    if(do_reversed)
    {
        ret_str = qh_data.value(FLD_STDB_NAME).toString() + " " + ret_str;
    }
    return ret_str;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_street_info(const QVariant &qv_index, bool do_reversed)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_STREETS, qv_index);
    return get_street_info(qh_data, do_reversed);
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_tenant_fio(const SLEE_HDB_REC &qh_data, bool full_fio)
{
    QString qstr_ret;
    qstr_ret = qh_data.value(FLD_TEN_FAM).toString();
    if(full_fio)
    {
        qstr_ret = qstr_ret + " " + qh_data.value(FLD_TEN_NAME).toString();
        qstr_ret = qstr_ret + " " + qh_data.value(FLD_TEN_PNAME).toString();
    }
    else
    {
        qstr_ret = qstr_ret + " " + qh_data.value(FLD_TEN_NAME).toString().at(0);
        qstr_ret = qstr_ret + "." + qh_data.value(FLD_TEN_PNAME).toString().at(0)+".";
    }
    return qstr_ret;
}


//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_tenant_dob_str(const SLEE_HDB_REC &qh_data, bool do_wide_format )
{
    QStringList qstr_m_list;
    qstr_m_list<<RUSTR("Января") \
               <<RUSTR("Февраля") \
               <<RUSTR("Марта") \
               <<RUSTR("Апреля") \
               <<RUSTR("Мая") \
               <<RUSTR("Июня") \
               <<RUSTR("Июля") \
               <<RUSTR("Августа") \
               <<RUSTR("Сентября") \
               <<RUSTR("Октября") \
               <<RUSTR("Ноября") \
               <<RUSTR("Декабря");
    QString qstr_ret;
    QDate qd_ret = qh_data[FLD_TEN_DOB].toDate();
    if(!do_wide_format)
    {
        qstr_ret = qd_ret.toString("dd.MM.yy");
        return qstr_ret;
    }
    qstr_ret.sprintf("\"%d\" ", qd_ret.day());
    qstr_ret += qstr_m_list.at(qd_ret.month()-1);
    qstr_ret += " ";
    qstr_ret += qd_ret.year();
    return qstr_ret;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::get_fam_member_fio(const QHash<int, QVariant> &qh_data, bool full_fio)
{
    QString qstr_ret;
    qstr_ret = qh_data.value(FLD_FAM_FAM).toString();
    if(full_fio)
    {
        qstr_ret = qstr_ret + " " + qh_data.value(FLD_FAM_NAME).toString();
        qstr_ret = qstr_ret + " " + qh_data.value(FLD_FAM_PNAME).toString();
    }
    else
    {
        qstr_ret = qstr_ret + " " + qh_data.value(FLD_FAM_NAME).toString().at(0);
        qstr_ret = qstr_ret + "." + qh_data.value(FLD_FAM_PNAME).toString().at(0)+".";
    }
    return qstr_ret;
}

//-----------------------------------------------------------------------------------------------------

void HousingDBMS::privatize_lspace(const QVariant &qv_a_index, const QStringList & qsl_spaces_ids, const QList<QDate> & qdl_pdates)
{

    SLEE_HDB_REC qh_data = get_data(LAKDB_APARTMENTS, qv_a_index);
    QVariantHash qvh_all_pdates = qh_data.value(FLD_APDB_PDATEHASH).toHash();
    for(int i = 0; i < qsl_spaces_ids.size(); i++)
    {
        QString qstr_id =  qsl_spaces_ids.at(i);
        qvh_all_pdates[qstr_id] = QVariant(qdl_pdates.at(i));
    }
    qh_data[FLD_APDB_PDATEHASH] = qvh_all_pdates;
    db_put(LAKDB_APARTMENTS, qh_data, qv_a_index);
}


//-----------------------------------------------------------------------------------------------------

double HousingDBMS::get_apartment_lspace(const QVariant &qv_a_index, const QStringList &qstrl_sp_ids, QStringList *p_qstrl_bad_ids)
{
    SLEE_HDB_REC qh_data = get_data(LAKDB_APARTMENTS, qv_a_index);
    QVariantHash qvh_spaces = qh_data.value(FLD_APDB_LSPSHASH).toHash();
    return get_apartment_lspace(qvh_spaces, qstrl_sp_ids,p_qstrl_bad_ids);
}

//-----------------------------------------------------------------------------------------------------

double HousingDBMS::get_apartment_lspace(const QVariantHash &qvh_lspaces, const QStringList &qstrl_sp_ids, QStringList *p_qstrl_bad_ids)
{
    double lspace = 0.0;
    if(!qstrl_sp_ids.size())
    {
        foreach(QVariant qv, qvh_lspaces)
        {
            lspace+=qv.toDouble();
        }
        return lspace;
    }

    foreach(QString qstr_id, qstrl_sp_ids)
    {
        if(!qvh_lspaces.contains(qstr_id))
        {
            lakhMsgE()<<RUSTR("HousingDBMS::get_apartment_lspace(). Расчет жилой площади квартиры. Несуществующий идентификатор жил.п.")<<qstr_id;
            qDebug()<<"HousingDBMS::get_apartment_lspace() ERROR LIST: "<<qvh_lspaces;
            if(p_qstrl_bad_ids)
                p_qstrl_bad_ids->append(qstr_id);
            continue;
        }
        lspace+=qvh_lspaces.value(qstr_id).toDouble();
    }
    return lspace;
}

//-----------------------------------------------------------------------------------------------------

QVariantList HousingDBMS::get_lease_for_ap(const QVariant &qv_ap_index, const QDate &qd)
{
    SLEE_HDB_REC_LST qhl_leases;
    QVariantList qvl_leases = get_db(LAKDB_LEASES)->findDataEq(FLD_LS_AINDEX,qv_ap_index,&qhl_leases);
    if(qd.isValid())
    {
        QVariantList qvl_ret;
        for(int i=0; i<qvl_leases.size(); i++)
        {
            if(qd > qhl_leases.at(i).value(FLD_LS_EDATE).toDate()) continue;
            if(qd < qhl_leases.at(i).value(FLD_LS_SDATE).toDate()) continue;
            qvl_ret.append(qvl_leases.at(i));
        }
        qvl_leases = qvl_ret;
    }
    return qvl_leases;
}

//-----------------------------------------------------------------------------------------------------


int HousingDBMS::get_leases_rooms_count(const SLEE_HDB_REC &qh_data)
{
    SLEE_HDB_REC qh_ap_data = get_data(LAKDB_APARTMENTS, qh_data.value(FLD_LS_AINDEX));
    int h_type = get_house_type(qh_ap_data.value(FLD_APDB_HSINDEX));
    QStringList qsl_s_indices = qh_data.value(FLD_LS_SPSLST).toStringList();
    int r_count = qh_ap_data.value(FLD_APDB_RCOUNT).toInt();
    switch(h_type)
    {
    case 0:
            return qh_ap_data.value(FLD_APDB_RCOUNT).toInt();
    case 1: return r_count;
    case 2: return qsl_s_indices.size();
    }
    return 0;
}

//-----------------------------------------------------------------------------------------------------

QString HousingDBMS::gen_hash_str_key()
{
    QString qstr_out;
    for(int i = 0; i<8; i++)
    {
        qstr_out+= QString::number(qrand() % 10);
    }
    return qstr_out;
}


//-----------------------------------------------------------------------------------------------------
